/* eslint-disable no-prototype-builtins */
const _ = require('lodash');
import { getBranches } from './validate';
let mainBranches = [];

const workflowNameDic = {
	BlankNode: '空白节点',
	detectLivingFace: '活体检测',
	faceCompare: '人脸比对',
	hangup: '结束模版',
	messageCollect: '信息记录',
	messageSure: '信息确认',
	requestLocation: '获取位置',
	scanCode: '推送扫码',
	sendMessage: '业务指令',
	signContract: '合同签署',
	speakWaiting: '播报话术',
	transfer: '转人工模板',
	ivvr: '业务导航',
	idCardCompare: '人证对比',
	sendForm: '表单填写',
	waitingRead: '查阅等待',
	sceneTransfer: '场景转接',
	summaryConfirm: '汇总确认',
	silentWating: '静默等待',
	optionJudge: '选项判断',
	messageSummary: '信息汇总',
	sendContents: '内容推送',
	nlp: 'NLP对接',
	soundReception: '收音模板',
	variableJudge: '变量判断',
	sendSign: '推送签名',
	sendDeposition: '证言抄录',
	variableCollection: '信息收集',
	inviteHumanThreeParties: '邀请人工三方',
	interfaceCall: '接口调用',
	collectShortVideo: '短视频采集',
	startSystemRecording: '开始双录',
	stopSystemRecording: '停止双录',
	cardCollection: '卡证收集',
	llm: 'LLM意图识别'
};

const getTitle = function (taskId, completed = true) {
	if (!taskId || taskId.length <= 0) {
		return '';
	}
	const parts = taskId.split('_');
	if (parts.length <= 0) {
		return '';
	}
	const workflowName = workflowNameDic[parts[0]];
	// console.log('taskId::', taskId);
	if (parts[0] === 'BlankNode') {
		return `${workflowName} ${parts[1]}`;
	}
	if (completed) {
		// return `流程${parts[1]} ${workflowName}`
		return `${workflowName} ${parts[1]}`;
	}
	return `${workflowName} ${parts[1]} (未编辑)`;
};

const getAllWorkstepName = function (worksteps, curTaskId = '') {
	let steps = [{ taskName: '新的流程节点', taskId: 'BlankNode' }];
	console.log(worksteps);
	_.forEach(worksteps, function (step) {
		if (typeof step !== 'object') {
			return;
		}
		if (step.id !== curTaskId) {
			// get task name from task id
			const taskName = getTitle(step.id, step.completed);
			steps.push({ taskId: step.id, taskName: taskName, completed: step.completed, type: step.type });
		}
	});
	// if (!worksteps || taskIds.length <= 0) {
	//     return steps
	// }
	// taskIds.map(taskId => {
	//     if (taskId !== curTaskId) {
	//         // get task name from task id
	//         const taskName = getTitle(taskId)
	//         steps.push({taskId: taskId, taskName: taskName})
	//     }
	// })
	return steps;
};

const getPreviewParent = function (worksteps, curTaskId = '') {
	if (typeof worksteps === 'string') {
		return;
	}
	for (let key in worksteps) {
		console.log(worksteps[key], curTaskId);
		if (worksteps[key].errorRemind || worksteps[key].answer) {
			if (worksteps[key].errorRemind.id === curTaskId || worksteps[key].answer.some(node => node.do.id === curTaskId)) {
				console.log(worksteps[key].id);
			}
		}
	}
};

// http://192.168.31.91:8090/pages/viewpage.action?pageId=1442724
const aiWorkflowRules = {
	// AI播报话术 - 信息确认/信息记录/人脸对比/扫码识别/位置信息
	aiBroadcast: {
		required: true,
		minlength: 1,
		maxlength: 140,
		type: String
	},
	// 等待时长 - 信息确认/信息记录/播报模板/扫码识别/位置信息
	waitTime: {
		required: true,
		min: 0,
		max: 99999,
		minlength: 1,
		maxlength: 5,
		type: Number
	},
	// 回答 - 信息确认/信息记录/人脸对比/活体检测/扫码识别/位置信息
	aiAnswer: {
		required: true,
		minlength: 1,
		maxlength: 280,
		type: String
	},
	// AI说 - 信息确认/信息记录/人脸对比/活体检测/扫码识别/位置信息
	aiTalk: {
		required: false,
		minlength: 1,
		maxlength: 140,
		type: String
	},
	// AI回复反问后，主流程提示话术
	aiProcessSpeech: {
		required: true,
		minlength: 1,
		maxlength: 200,
		type: String
	},
	// 最大允许次数 - 信息确认/信息记录/扫码识别/位置信息
	maxTalkTimes: {
		required: true,
		min: 0,
		max: 99,
		minlength: 1,
		maxlength: 2,
		type: Number
	},
	// 检测次数 - 人脸比对
	detectTimes: {
		required: true,
		min: 0,
		max: 99,
		type: Number
	},
	// 重试次数 - 活体检测
	repeatTimes: {
		required: true,
		min: 0,
		max: 99,
		type: Number
	},
	// 阈值 - 活体检测
	confidence: {
		required: true,
		min: 0,
		max: 100,
		type: Number
	},
	// 等待时长 - 表单填写/静默等待模块
	waitOverTime: {
		required: true,
		min: 0,
		max: 9999999,
		minlength: 1,
		maxlength: 7,
		type: Number
	},
	// AI播报话术 ai 说
	aiWords: {
		required: true,
		minlength: 1,
		maxlength: 1000,
		type: String
	}
};
const workflowTemplete = {
	// 结束模板数据
	hangupNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			data: '',
			command: 'speak'
		},
		after: {
			data: {
				reason: ''
			},
			command: 'hangup'
		},
		type: 'sendMessage',
		no: '',
		id: '',
		isEnd: 'true',
		isCompleted: false
	},
	// 信息确认数据结构  实时拟合_视频原生方式
	messageSureNode: {
		needHearing: '',
		robotProperty: {
			needHearing: true, //是否需要收音
			videoType: '', //real-time-fitting 4个
			audioType: '' //video-native
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		no: '',
		id: '',
		type: 'messageSure',
		after: {
			data: {
				judgmentType: 'positive-negative',
				speechBeginWaitingTimeMS: ''
			},
			command: 'listen'
		},
		intelligentReply: {
			isOn: false,
			data: [],
			mainProcessSpeech: '',
			speechBeginWaitingTimeMS: '',
			variables: [],
			variableId: ''
		},
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'others',
				type: 'other',
				after: {
					command: 'listen'
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'positive',
				enableNluLibrary: true,
				amendRules: [],
				type: 'yes',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'negative',
				enableNluLibrary: true,
				amendRules: [],
				type: 'no',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				key: 'empty',
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				after: {
					data: {
						judgmentType: 'positive-negative',
						speechBeginWaitingTimeMS: 3000
					},
					command: 'listen'
				}
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				no: '',
				id: ''
			},
			maximum: 0
		},
		emptyErrorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			maximum: 0,
			type: 'noAnswers'
		}
	},

	// 转人工数据结构
	transferNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			command: 'send-message',
			data: {
				type: 'peer-cmd',
				data: {
					cmd: 'transfer',
					data: {
						reason: ''
					}
				}
			}
		},
		no: '',
		type: 'sendMessage',
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'hangup',
					data: {
						reason: 'quietly'
					}
				}
			},
			{
				key: 'others',
				do: {
					command: 'speak',
					data: '转人工失败'
				},
				after: {
					command: 'hangup'
				}
			}
		],
		isEnd: 'true',
		isCompleted: false
	},
	//播报模版
	speakWaitingNode: {
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		no: '',
		id: '',
		type: 'speakWait',
		after: {
			data: '',
			command: 'wait',
			no: '',
			id: '',
			type: 'yes',
			after: {
				command: 'jump-to',
				no: '',
				id: '',
				type: 'yes'
			}
		}
	},
	// 人脸比对数据结构
	faceCompareNode: {
		robotProperty: {
			needHearing: false,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		no: '',
		id: '',
		type: 'faceCompare',
		after: {
			data: {
				confidence: ''
			},
			command: 'toCompareFace'
		},
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'yes',
				type: 'yes',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'no',
				type: 'no',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'others',
				type: 'other',
				after: {
					command: 'toCompareFace'
				}
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				no: '',
				id: ''
			},
			maximum: 0
		}
	},
	// 人证对比信息
	idCardCompareNode: {
		robotProperty: {
			needHearing: false,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		no: '',
		id: '',
		type: 'idCardCompare',
		after: {
			data: {
				confidence: ''
			},
			command: 'toMatchIdCardNoAndPic'
		},
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'yes',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				},
				type: 'yes'
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'no',
				after: {
					data: {
						confidence: ''
					},
					command: 'toMatchIdCardNoAndPic'
				},
				type: 'no'
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				no: '',
				id: ''
			},
			type: 'no',
			maximum: 0
		}
	},
	//信息采集模版
	messageCollectNode: {
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		id: '',
		no: '',
		type: 'messageCollect',
		after: {
			data: {
				judgmentType: 'non-empty',
				speechBeginWaitingTimeMS: 0
			},
			command: 'listen'
		},
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['not-empty'],
				type: 'yes',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'others',
				type: 'no',
				after: {
					data: {
						judgmentType: 'non-empty',
						speechBeginWaitingTimeMS: '3000'
					},
					command: 'listen'
				}
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				no: '',
				id: ''
			},
			type: 'others',
			maximum: ''
		},
		typeOfCollectedResults: '',
		associatedVariable: {
			id: '',
			name: '',
			describe: '',
			initMode: '',
			type: ''
		},
		robotProperty: {
			needHearing: true,
			videoType: '',
			audioType: ''
		}
	},
	//扫码模版
	scanCodeNode: {
		robotProperty: {
			needHearing: false,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		id: '',
		no: '',
		type: 'scanCode',
		after: {
			data: {
				data: {
					cmd: 'scan-code'
				},
				type: 'peer-cmd'
			},
			command: 'send-message',
			after: {
				command: 'receive-message'
			}
		},
		timeout: '',
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['ok'],
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				},
				type: 'yes'
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['reject', 'error', 'cancel'],
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				},
				type: 'no'
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['timeout'],
				after: {
					command: 'to-compare-answer',
					data: ''
				},
				type: 'other'
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				no: '',
				id: ''
			},
			maximum: '',
			timeout: '',
			type: 'other'
		}
	},
	// 推送视频指令
	sendVideoNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			command: 'send-message',
			data: {
				type: 'link',
				data: {
					url: '',
					type: 'video',
					name: '',
					size: 0
				}
			}
		},
		id: '',
		no: '',
		type: 'sendVideoNode',
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				key: 'others',
				do: {
					command: 'send-message',
					data: {
						type: 'link',
						data: {
							url: '',
							type: 'video',
							name: '',
							size: 0
						}
					}
				},
				after: {
					command: 'receive-message'
				}
			}
		],
		errorRemind: {
			maximum: 3
		}
	},
	// 推送网页指令
	sendPageNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			command: 'send-message',
			data: {
				type: 'link',
				data: {
					url: '',
					type: 'page',
					name: ''
				}
			}
		},
		no: '',
		id: '',
		type: 'sendLinkNode',
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				key: 'others',
				do: {
					command: 'send-message',
					data: {
						type: 'link',
						data: {
							url: '',
							type: 'page',
							name: ''
						}
					}
				},
				after: {
					command: 'receive-message'
				}
			}
		],
		errorRemind: {
			maximum: 3
		}
	},
	// 推送截图指令
	screenSnapshotNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			command: 'send-message',
			data: {
				type: 'peer-cmd',
				data: {
					cmd: 'screen-snapshot'
				}
			}
		},
		no: '',
		id: '',
		type: 'sendMessage',
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				key: 'others',
				do: {
					command: 'send-message',
					data: {
						type: 'peer-cmd',
						data: {
							cmd: 'screen-snapshot'
						}
					}
				},
				after: {
					command: 'receive-message'
				}
			}
		],
		errorRemind: {
			maximum: 3
		}
	},

	// 推送富文本指令
	sendRichTextNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		id: '',
		no: '',
		type: 'sendRichTextNode',
		do: {
			command: 'send-message',
			data: {
				type: 'richtext',
				data: {
					content: '',
					attachments: [
						{
							name: '',
							size: '',
							url: ''
						}
					]
				}
			},
			variables: [],
			variableId: ''
		},
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'jump-to',
					id: '',
					no: ''
				}
			},
			{
				key: 'others',
				do: {
					command: 'send-message',
					data: {
						type: 'richtext',
						data: {
							content: '',
							attachments: [
								{
									name: '',
									size: '',
									url: ''
								}
							]
						}
					}
				},
				after: {
					command: 'receive-message'
				}
			}
		],
		errorRemind: {
			maximum: 3
		}
	},
	// 推送Pdf
	sendPdfNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			command: 'send-message',
			data: {
				type: 'link',
				data: {
					urlKey: '',
					url: '',
					type: 'paper',
					name: '',
					size: 0
				}
			}
		},
		no: '',
		id: '',
		type: 'signContract',
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				key: 'others',
				do: {
					command: 'send-message',
					data: {
						type: 'link',
						data: {
							urlKey: '',
							url: '',
							type: 'paper',
							name: '',
							size: 0
						}
					}
				},
				after: {
					command: 'receive-message'
				}
			}
		],
		errorRemind: {
			maximum: 3
		}
	},
	// 合同签署
	signContractNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			command: 'send-message',
			data: {
				type: 'link',
				data: {
					urlKey: '',
					url: '',
					type: 'paper',
					name: '',
					size: 0
				}
			}
		},
		no: '',
		id: '',
		type: 'signContract',
		after: {
			command: 'receive-message'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					command: 'jump-to',
					no: '',
					id: ''
				}
			},
			{
				key: 'others',
				do: {
					command: 'send-message',
					data: {
						type: 'link',
						data: {
							urlKey: '',
							url: '',
							type: 'paper',
							name: '',
							size: 0
						}
					}
				},
				after: {
					command: 'receive-message'
				}
			}
		],
		errorRemind: {
			maximum: 3
		}
	},
	// 获取地理位置数据结构
	requestLocationNode: {
		robotProperty: {
			needHearing: false,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		no: '',
		id: '',
		type: 'requestLocation',
		after: {
			command: 'requestLocation'
		},
		timeout: 0,
		typeOfCollectedResults: 'text',
		associatedVariable: {
			id: '',
			name: '',
			describe: '',
			initMode: '',
			type: ''
		},
		requestLocation: {
			do: {
				data: {
					data: {
						cmd: 'request-location'
					},
					type: 'peer-cmd'
				},
				command: 'send-message'
			},
			type: 'requestLocation',
			after: {
				command: 'receive-message'
			},
			answer: [
				{
					do: {
						data: '',
						command: 'speak',
						variables: [],
						variableId: ''
					},
					key: ['ok'],
					after: {
						command: 'jump-to',
						no: '',
						id: ''
					},
					type: 'yes'
				},
				{
					do: {
						data: '',
						command: 'speak',
						variables: [],
						variableId: ''
					},
					key: ['reject'],
					after: {
						command: 'jump-to',
						no: '',
						id: ''
					},
					type: 'no'
				},
				{
					do: {
						data: '',
						command: 'speak',
						variables: [],
						variableId: ''
					},
					key: ['timeout'],
					after: {
						data: '',
						command: 'to-compare-answer'
					},
					type: 'other'
				}
			],
			timeout: 0,
			errorRemind: {
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				},
				maximum: 0
			},
			no: '',
			id: ''
		}
	},
	//活体检测
	detectLivingFaceNode: {
		robotProperty: {
			needHearing: false,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		name: '',
		do: {
			data: {
				confidence: ''
			},
			command: 'toDetectLivingFace'
		},
		no: '',
		id: '',
		type: 'detectLivingFace',
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'yes',
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				},
				type: 'yes'
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'no',
				after: {
					data: {
						confidence: ''
					},
					command: 'toDetectLivingFace'
				},
				type: 'no'
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				no: '',
				id: ''
			},
			type: 'no',
			maximum: ''
		}
	},
	// 业务导航
	ivvrNode: {
		robotProperty: {
			needHearing: true, //是否需要收音
			videoType: '', // ['real-time-fitting', 'pre-recorded-video', 'video-switching', 'no-video']
			audioType: '' //['video-native', 'tts']
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		id: '',
		no: '',
		type: 'ivvr',
		after: {
			data: {
				judgmentType: 'positive-negative',
				speechBeginWaitingTimeMS: 0
			},
			command: 'listen'
		},
		intelligentReply: {
			isOn: true,
			data: [
				{
					name: '',
					reply: ''
				},
				{
					name: '',
					reply: ''
				}
			],
			mainProcessSpeech: '',
			speechBeginWaitingTimeMS: 0,
			variables: [],
			variableId: ''
		},
		intentLib: '',
		answer: [],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			maximum: 0
		},
		emptyErrorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			maximum: 0,
			type: 'noAnswers'
		}
	},
	// 表单填写
	sendForm: {
		robotProperty: {
			needHearing: false,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		name: '',
		id: '',
		no: '',
		type: 'sendFormNode',
		timeout: 0,
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		after: {
			command: 'send-message',
			data: {
				data: {
					data: {
						formId: ''
					},
					cmd: 'send-form'
				},
				type: 'peer-cmd'
			},
			after: {
				command: 'receive-message'
			}
		},
		answer: [
			{
				key: ['ok'],
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				after: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'ok'
			},
			{
				key: ['reject'],
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				after: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'reject'
			},
			{
				key: ['timeout'],
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				after: {
					command: 'to-compare-answer',
					data: ''
				},
				type: 'timeout'
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			type: 'over',
			maximum: 0,
			timeout: 0
		}
	},
	// 查阅等待
	waitingRead: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		id: '',
		no: '',
		type: 'waitingReadNode',
		fromNode: '',
		timeout: 0,
		do: {
			data: '',
			command: 'speak'
		},
		after: {
			command: 'send-message',
			data: {
				data: {
					cmd: 'waiting-read'
				},
				type: 'peer-cmd'
			},
			after: {
				command: 'receive-message'
			},
			type: 'ok'
		},
		answer: [
			{
				key: ['ok'],
				do: {
					data: '',
					command: 'speak'
				},
				after: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'ok'
			},
			{
				key: ['timeout'],
				do: {
					data: '',
					command: 'speak'
				},
				after: {
					command: 'to-compare-answer',
					data: ''
				},
				type: 'timeout'
			},
			/** 查阅等待不确定回答 */
			{
				key: ['reject'],
				do: {
					data: '',
					command: 'speak'
				},
				after: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'reject'
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak'
			},
			after: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			type: 'overTime',
			maximum: 0,
			timeout: 0
		}
	},
	// 场景转接数据
	sceneTransfer: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		id: '',
		no: '',
		type: 'bizScenTransferNode',
		do: {
			command: 'send-message',
			data: {
				data: {
					data: {
						bizSceneId: 0,
						bizSceneName: ''
					},
					cmd: 'transfer'
				},
				type: 'peer-cmd'
			},
			after: {
				command: 'receive-message'
			}
		},
		answer: [
			{
				do: {
					data: {
						reason: 'quietly'
					},
					command: 'hangup'
				},
				key: ['ok'],
				type: 'ok'
			},
			{
				do: {
					data: {
						reason: ''
					},
					command: 'hangup'
				},
				type: 'reject',
				key: 'others'
			}
		],
		isEnd: 'true'
	},
	// 汇总确认
	summaryConfirmNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		id: '',
		no: '',
		type: 'summaryConfirmNode',
		timeout: 0,
		summary: {
			title: '',
			items: [
				{
					node: '',
					name: ''
				}
			]
		},
		do: {
			command: 'speak',
			data: '',
			variables: [],
			variableId: ''
		},
		after: {
			command: 'send-message',
			data: {
				data: {
					data: '',
					cmd: 'show-summary-confirm'
				},
				type: 'peer-cmd'
			},
			after: {
				data: {
					judgmentType: 'positive-negative',
					speechBeginWaitingTimeMS: 3000
				},
				command: 'listen'
			}
		},
		intelligentReply: {
			isOn: true,
			data: [
				{
					name: '',
					reply: ''
				},
				{
					name: '',
					reply: ''
				}
			],
			mainProcessSpeech: '',
			speechBeginWaitingTimeMS: 3000
		},
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'others',
				type: 'other',
				after: {
					data: {
						judgmentType: 'positive-negative',
						speechBeginWaitingTimeMS: 0
					},
					command: 'listen'
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'positive',
				enableNluLibrary: true,
				amendRules: [],
				type: 'yes',
				after: {
					command: 'send-message',
					data: {
						data: {
							data: {
								type: 'yes'
							},
							cmd: 'close-summary-confirm'
						},
						type: 'peer-cmd'
					},
					after: {
						command: 'jump-to',
						id: '',
						no: ''
					}
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: 'negative',
				enableNluLibrary: true,
				amendRules: [],
				type: 'noSure',
				after: {
					command: 'send-message',
					data: {
						data: {
							data: {
								type: 'no'
							},
							cmd: 'close-summary-confirm'
						},
						type: 'peer-cmd'
					},
					after: {
						command: 'jump-to',
						id: '',
						no: ''
					}
				}
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				id: '',
				no: '',
				command: 'jump-to'
			},
			type: 'overTime',
			maximum: 0
		}
	},
	// 静默等待
	silentWatingNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		id: '',
		no: '',
		type: 'silentWating',
		after: {
			data: 0,
			command: 'wait',
			no: '',
			id: '',
			type: 'yes',
			after: {
				command: 'jump-to',
				no: '',
				id: '',
				type: 'yes'
			}
		}
	},
	// 选项判断
	optionJudgeNode: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		do: {
			data: '',
			command: 'speak'
		},
		id: '',
		no: '',
		type: 'optionJudgment',
		timeout: '3000',
		after: {
			data: {
				data: {
					cmd: 'option-judgment'
				},
				type: 'peer-cmd'
			},
			command: 'send-message',
			after: {
				command: 'receive-message'
			}
		},
		answer: [
			{
				key: [],
				do: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'option'
			},
			{
				key: [],
				do: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'yes'
			},
			{
				key: ['timeout'],
				do: {
					id: '',
					no: ''
				},
				type: 'timeout'
			}
		],
		errorRemind: {
			do: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			type: 'others',
			maximum: 0
		}
	},

	// 信息汇总
	messageSummary: {
		robotProperty: {
			needHearing: 'preorder',
			videoType: 'preorder',
			audioType: 'preorder'
		},
		name: '',
		no: '',
		id: '',
		type: 'messageSummary',
		summary: {
			items: [
				{
					node: '',
					name: ''
				}
			]
		},
		do: {
			command: 'storageMessageSummary'
		},
		after: {
			command: 'jump-to',
			no: '',
			id: '',
			type: 'yes'
		},
		notifyExternal: {
			url: '',
			method: 'post',
			params: [
				{
					name: '',
					initMode: ''
				}
			]
		}
	},
	// NLP对接模板
	nlpNode: {
		robotProperty: {
			needHearing: true,
			videoType: 'real-time-fitting',
			audioType: 'video-native'
		},
		do: {
			data: {
				judgmentType: 'positive-negative',
				speechBeginWaitingTimeMS: 0
			},
			command: 'listen'
		},
		id: '',
		no: '',
		name: '',
		type: 'third-nlp',
		answer: [],
		nlp: '',
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			type: 'error',
			after: {
				id: '',
				no: '',
				command: 'jump-to'
			},
			maximum: 0
		}
	},
	// 收音模板
	soundReceptionNode: {
		id: '',
		type: 'soundReception',
		robotProperty: {
			needHearing: true,
			videoType: 'preorder',
			audioType: 'preorder'
		},
		do: {
			command: 'listen',
			data: {
				judgmentType: 'non-empty',
				speechBeginWaitingTimeMS: '3000'
			}
		},
		answer: [
			{
				key: ['not-empty'],
				do: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'ok'
			},
			{
				key: 'others',
				do: {
					command: 'listen',
					data: {
						judgmentType: 'non-empty',
						speechBeginWaitingTimeMS: '3000'
					}
				}
			}
		],
		errorRemind: {
			do: {
				command: 'jump-to',
				id: '',
				no: ''
			},
			type: 'no',
			maximum: 3
		},
		typeOfCollectedResults: '',
		associatedVariable: {
			id: '',
			name: '',
			describe: '',
			initMode: '',
			type: ''
		}
	},
	// 变量判断模板数据
	variableJudge: {
		id: '',
		no: '',
		type: 'variableJudgment',
		robotProperty: {
			needHearing: true,
			videoType: 'preorder',
			audioType: 'preorder'
		},
		do: {
			command: 'to-compare-answer'
		},
		answer: [
			// 其他分支
			{
				key: 'others',
				do: {
					command: 'jump-to',
					id: '',
					no: ''
				},
				type: 'others'
			}
		]
	},
	//证言抄录模版
	sendDeposition: {
		do: {
			data: '', //证言抄录AI播报话术
			command: 'speak',
			variables: [], //添加的变量
			variableId: ''
		},
		id: '',
		no: '',
		name: '',
		type: 'testimonyWrite',
		after: {
			data: {
				data: {
					cmd: 'testimony-write',
					data: {
						testimonyId: 0, // 选择抄录的id
						type: 'testimony-write'
					}
				},
				type: 'peer-cmd'
			},
			command: 'send-message',
			after: {
				command: 'receive-message'
			}
		},
		answer: [
			{
				do: {
					data: '', //等待超时AI说
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['timeout'],
				type: 'other',
				after: {
					data: '',
					command: 'to-compare-answer'
				}
			},
			{
				do: {
					data: '', //提交失败AI说
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['reject'],
				type: 'no',
				after: {
					id: '',
					no: '',
					type: 'no',
					command: 'jump-to'
				}
			},
			{
				do: {
					data: '', //等待超时AI说
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['timeout'],
				type: 'other',
				after: {
					data: '',
					command: 'to-compare-answer'
				}
			}
		],
		errorRemind: {
			do: {
				data: '', //最后面下一步等待超时AI说
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				id: '',
				no: '',
				command: 'jump-to'
			},
			type: 'timeout',
			maximum: 0, //等待超时超过最大次数
			timeout: 0 //超时等待时长
		},
		timeout: 0, //等待时长
		robotProperty: {
			audioType: 'video-native',
			videoType: 'real-time-fitting',
			needHearing: false
		},
		associatedVariable: {}, //关联变量
		typeOfCollectedResults: 'text' //收集结果数据类型
	},
	//推送签名模版
	sendSign: {
		do: {
			data: '', //推送签名AI说
			command: 'speak',
			variables: [], //添加的变量
			variableId: ''
		},
		id: '',
		no: '',
		name: '',
		type: 'handWriting',
		after: {
			command: 'handWriting'
		},
		timeout: 0, //等待时长
		robotProperty: {
			audioType: 'video-native',
			videoType: 'real-time-fitting',
			needHearing: false
		},
		handWriting: {
			do: {
				data: {
					cmd: 'get-handwriting'
				},
				type: 'handwriting'
			},
			after: {
				command: 'receive-message'
			},
			answer: [
				{
					do: {
						data: '', //提交成功AI说
						command: 'speak',
						variables: [],
						variableId: ''
					},
					key: ['ok'],
					type: 'yes',
					after: {
						id: '',
						no: '',
						type: 'yes',
						command: 'jump-to'
					}
				},
				{
					do: {
						data: '', //提交失败AI说
						command: 'speak',
						variables: [],
						variableId: ''
					},
					key: ['reject'],
					type: 'no',
					after: {
						id: '',
						no: '',
						type: 'no',
						command: 'jump-to'
					}
				},
				{
					do: {
						data: '', //等待超时AI说
						command: 'speak',
						variables: [],
						variableId: ''
					},
					key: ['timeout'],
					type: 'other',
					after: {
						data: '',
						command: 'to-compare-answer'
					}
				}
			],
			timeout: 0, //超时等待时长
			errorRemind: {
				do: {
					data: '', //最后面下一步等待超时AI说
					command: 'speak',
					variables: [],
					variableId: ''
				},
				after: {
					id: '',
					no: '',
					command: 'jump-to'
				},
				maximum: 0, //等待超时超过最大次数
				type: 'timeout'
			}
		},
		associatedVariable: {},
		typeOfCollectedResults: 'text'
	},
	// 信息收集模板数据
	variableCollection: {
		do: {
			data: '',
			command: 'speak',
			variables: [],
			variableId: ''
		},
		id: '',
		no: '',
		name: '',
		type: 'collectVariable',
		after: {
			data: {
				data: {
					cmd: 'collect-variable',
					data: {
						allChecked: false, //是否全选
						variables: [] //收集的变量数据
					}
				},
				type: 'peer-cmd'
			},
			command: 'send-message',
			after: {
				command: 'receive-message'
			}
		},
		timeout: 0,
		answer: [
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['ok'],
				type: 'yes',
				after: {
					id: '',
					no: '',
					type: 'yes',
					command: 'jump-to'
				}
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['reject', 'error', 'cancel'],
				after: {
					command: 'jump-to',
					no: '',
					id: ''
				},
				type: 'no'
			},
			{
				do: {
					data: '',
					command: 'speak',
					variables: [],
					variableId: ''
				},
				key: ['timeout'],
				after: {
					command: 'to-compare-answer',
					data: ''
				},
				type: 'other'
			}
		],
		errorRemind: {
			do: {
				data: '',
				command: 'speak',
				variables: [],
				variableId: ''
			},
			after: {
				id: '',
				no: '',
				command: 'jump-to'
			},
			timeout: 0,
			maximum: 3,
			type: 'other'
		}
	},
	// 邀请人工三方数据
	inviteHumanThreeParties: {
		// TODO 后面更新数据结构
	},
	// todo 接口调用数据结构
	interfaceCall: {}
};

// nextNode = {parentId, type='yes', no}
const updateNodeId = function (step, nextNode) {
	console.log('####updateNodeId : ', step, nextNode);

	if (!step || !nextNode) {
		console.error('updateNodeId error : ', step, nextNode);
		return;
	}

	if (step.type === 'speakWait') {
		step.after.no = nextNode.no;
		step.after.id = nextNode.no;
		step.after.after.no = nextNode.no;
		step.after.after.id = nextNode.no;
		return;
	}
	// 静默等待模块数据结构只有一层after
	if (step.type === 'silentWating') {
		if (step.after) {
			step.after.no = nextNode.no;
			step.after.id = nextNode.no;
			step.after.after.no = nextNode.no;
			step.after.after.id = nextNode.no;
		} else {
			step.do.no = nextNode.no;
			step.do.id = nextNode.no;
			step.do.after.no = nextNode.no;
			step.do.after.id = nextNode.no;
		}

		return;
	}

	// 为了解决 testimonyWrite:{  里面嵌套了 answer:[] errorRemind :{}} 否则就要到workflow.js  写 step.testimonyWrite
	// if (step.type === 'testimonyWrite') {
	// 	console.warn('testimonyWrite', step);
	// 	if (step.testimonyWrite) {
	// 		step.testimonyWrite.errorRemind.do.no = nextNode.no;
	// 		step.testimonyWrite.errorRemind.do.id = nextNode.no;
	// 		step.testimonyWrite.errorRemind.after.no = nextNode.no;
	// 		step.testimonyWrite.errorRemind.after.id = nextNode.no;

	// 	} else {
	// 		step.testimonyWrite.no = nextNode.no;
	// 		step.testimonyWrite.id = nextNode.no;
	// 		step.testimonyWrite.errorRemind.do.no = nextNode.no;
	// 		step.testimonyWrite.errorRemind.do.id = nextNode.no;
	// 	}
	// 	return;
	// }

	_.forEach(step, function (value) {
		// scanCodeNode->scanCode->errorRemind->do->no type:null,
		// scanCodeNode->scanCode->answer[0]->answer[0]->do->no type:'yes',
		// scanCodeNode->scanCode->answer[0]->answer[1]->do->no type:'no',
		// scanCodeNode->scanCode->answer[0]->answer[2]->do->no type:'other',
		// scanCodeNode->scanCode->answer[1]->do->no type:'reject',
		if (_.isObject(value)) {
			// console.log('updateNodeId : ', value, nextNode);
			// console.log('value:', value);
			// console.log('nextNode:', nextNode);
			if (value.type === nextNode.type) {
				if (value.do && value.do.command === 'jump-to') {
					value.do.no = nextNode.no;
					value.do.id = nextNode.no;
				} else if (value.after && value.after.command === 'jump-to') {
					value.after.no = nextNode.no;
					value.after.id = nextNode.no;
				} else if (value.command === 'jump-to') {
					value.no = nextNode.no;
					value.id = nextNode.no;
				} else if (value.after && value.after.after && value.after.after.command === 'jump-to') {
					value.after.after.no = nextNode.no;
					value.after.after.id = nextNode.no;
				} else if (value.do && value.do.after && value.do.after.command === 'jump-to') {
					value.do.after.no = nextNode.no;
					value.do.after.id = nextNode.no;
				}
			}
		}
		if (_.isArray(value)) {
			value.map(item => {
				if (_.isObject(item)) {
					if (item.answer && _.isArray(item.answer)) {
						item.answer.map(answer => {
							if (answer.type === nextNode.type) {
								if (answer.do && answer.do.command === 'jump-to') {
									answer.do.no = nextNode.no;
									answer.do.id = nextNode.no;
								} else if (answer.after && answer.after.command === 'jump-to') {
									answer.after.no = nextNode.no;
									answer.after.id = nextNode.no;
								} else if (answer.after.after && answer.after.after.command === 'jump-to') {
									answer.after.after.no = nextNode.no;
									answer.after.after.id = nextNode.no;
								} else if (answer.do.after && answer.do.after.command === 'jump-to') {
									answer.do.after.no = nextNode.no;
									answer.do.after.id = nextNode.no;
								}
							}
						});
					} else if (item.type === nextNode.type) {
						if (item.do && item.do.command === 'jump-to') {
							item.do.no = nextNode.no;
							item.do.id = nextNode.no;
						} else if (item.after && item.after.command === 'jump-to') {
							item.after.no = nextNode.no;
							item.after.id = nextNode.no;
						} else if (item.after && item.after.after && item.after.after.command === 'jump-to') {
							item.after.after.no = nextNode.no;
							item.after.after.id = nextNode.no;
						} else if (item.do && item.do.after && item.do.after.command === 'jump-to') {
							item.do.after.no = nextNode.no;
							item.do.after.id = nextNode.no;
						}
					}
				}
			});
		}
	});

	// 选项判断模板，answer的最后一项timeout里面的ID必须是errorRemind 里面的ID
	if (step.type === 'optionJudgment') {
		step.answer.forEach(item => {
			if (item.type === 'timeout') {
				item.do.id = step.errorRemind.do.id;
				item.do.no = step.errorRemind.do.no;
			}
		});
	}

	if (step.type === 'optionJudgment') {
		step.answer.forEach(item => {
			if (item.type === 'timeout') {
				item.do.id = step.errorRemind.do.id;
				item.do.no = step.errorRemind.do.no;
			}
		});
	}

	console.log('###step:', step);
};
// nextNode = {parentId, oldId, newId}
const replaceNodeId = function (step, nextNode) {
	if (!step || !nextNode) {
		console.error('replaceNodeId error : ', step, nextNode);
		return;
	}
	if (step.type === 'speakWait') {
		step.after.no = nextNode.newId;
		step.after.id = nextNode.newId;
		step.after.after.no = nextNode.newId;
		step.after.after.id = nextNode.newId;
		return;
	}
	// 静默等待模块数据结构只有一层after
	if (step.type === 'silentWating') {
		if (step.after) {
			step.after.no = nextNode.newId;
			step.after.id = nextNode.newId;
			step.after.after.no = nextNode.newId;
			step.after.after.id = nextNode.newId;
		} else {
			step.do.no = nextNode.newId;
			step.do.id = nextNode.newId;
			step.do.after.no = nextNode.newId;
			step.do.after.id = nextNode.newId;
		}
		return;
	}

	_.forEach(step, function (value) {
		if (_.isObject(value)) {
			if (value.do && value.do.command === 'jump-to' && value.do.id === nextNode.oldId) {
				value.do.no = nextNode.newId;
				value.do.id = nextNode.newId;
			} else if (value.after && value.after.command === 'jump-to' && value.after.id === nextNode.oldId) {
				value.after.no = nextNode.newId;
				value.after.id = nextNode.newId;
			} else if (value.command === 'jump-to') {
				value.no = nextNode.newId;
				value.id = nextNode.newId;
			} else if (value.after && value.after.after && value.after.after.command === 'jump-to' && value.after.after.id === nextNode.oldId) {
				value.after.after.no = nextNode.newId;
				value.after.after.id = nextNode.newId;
			} else if (value.do && value.do.after && value.do.after.command === 'jump-to' && value.do.after.id === nextNode.oldId) {
				value.do.after.no = nextNode.newId;
				value.do.after.id = nextNode.newId;
			}
		}
		if (_.isArray(value)) {
			value.map(item => {
				if (item.answer && _.isArray(item.answer)) {
					item.answer.map(answer => {
						if (answer.do && answer.do.command === 'jump-to' && answer.do.id === nextNode.oldId) {
							answer.do.no = nextNode.newId;
							answer.do.id = nextNode.newId;
						} else if (answer.after && answer.after.command === 'jump-to' && answer.after.id === nextNode.oldId) {
							answer.after.no = nextNode.newId;
							answer.after.id = nextNode.newId;
						} else if (answer.after && answer.after.after.command === 'jump-to' && answer.after.after.id === nextNode.oldId) {
							answer.after.after.no = nextNode.newId;
							answer.after.after.id = nextNode.newId;
						} else if (answer.do && answer.do.after.command === 'jump-to' && answer.do.after.id === nextNode.oldId) {
							answer.do.after.no = nextNode.newId;
							answer.do.after.id = nextNode.newId;
						}
					});
				} else if (item.do && item.do.command === 'jump-to' && (item.do.no === nextNode.oldId || item.do.id === nextNode.oldId)) {
					item.do.no = nextNode.newId;
					item.do.id = nextNode.newId;
				} else if (item.after && item.after.command === 'jump-to' && item.after.id === nextNode.oldId) {
					item.after.no = nextNode.newId;
					item.after.id = nextNode.newId;
				} else if (item.after && item.after.after && item.after.after.command === 'jump-to' && item.after.after.id === nextNode.oldId) {
					item.after.after.no = nextNode.newId;
					item.after.after.id = nextNode.newId;
				} else if (item.do && item.do.after && item.do.after.command === 'jump-to' && item.do.after.id === nextNode.oldId) {
					item.do.after.no = nextNode.newId;
					item.do.after.id = nextNode.newId;
				}
			});
		}
	});

	if (step.type === 'optionJudgment') {
		step.answer.forEach(item => {
			if (item.type === 'timeout') {
				item.do.id = step.errorRemind.do.id;
				item.do.no = step.errorRemind.do.no;
			}
		});
	}
};
// verify step whether completed
const verifyCompletion = function (step, steps, removedStepId, type) {
	if (!step || !step.id) {
		return false;
	}
	if (step.id.includes('transfer') || step.id.includes('hangup') || step.id.includes('BlankNode')) {
		return step.completed;
	}
	let completed = step.completed;

	// 流程中关联变量作废
	if (completed && type === 'VERIFY_INVALID_ARIABLE') {
		const invalidVariableList = window.store.state.associatedVariable.invalidVariableList;
		let variableName = '';
		// 判断是否关联该变量
		if (step.id.includes('messageCollect') || step.id.includes('soundReception') || step.id.includes('requestLocation')) {
			if (step && step.associatedVariable) {
				variableName = step.associatedVariable.name;
			}
		}
		if (variableName && variableName.length > 0) {
			for (let variable of invalidVariableList) {
				if (variableName === variable.name) {
					completed = false;
					break;
				}
			}
		}
	}
	// eslint-disable-next-line no-param-reassign
	step = step.scanCode || step.requestLocation || step;
	// eslint-disable-next-line vars-on-top
	const flatten = function (item) {
		if (item) {
			return [item, _.flatMapDeep(item.answer, flatten)];
		}
		return [];
	};
	const result = _.flatMapDeep(step, flatten);

	console.log('result : ', result);

	if (removedStepId) {
		let filtered = [];
		_.filter(result, function (item) {
			if (item.answer && _.isArray(item.answer)) {
				item.answer.map(answer => {
					// eslint-disable-next-line no-extra-parens
					if ((answer.do && item.answer.command === 'jump-to') || (answer.after && answer.after.command === 'jump-to') || answer.command === 'jump-to') {
						filtered.push(answer);
					}
				});
				// eslint-disable-next-line no-extra-parens
			} else if (
				(item.do && item.do.command === 'jump-to') ||
				(item.after && item.after.command === 'jump-to') ||
				item.command === 'jump-to' ||
				(item.do && item.do.after && item.do.after.command === 'jump-to') ||
				(item.after && item.after.after && item.after.after.command === 'jump-to')
			) {
				filtered.push(item);
			}
		});

		console.log('filtered : ', filtered);

		filtered.map(item => {
			if (item.after && item.after.id === removedStepId) {
				completed = false;
				// 删除节点时，置空关联的父节点
				if (!steps[item.after.id] && type === 'REMOVE_WORK_STEP') {
					console.log('filtered : ', steps, item);
					item.after.id = '';
					item.after.no = '';
				}
			} else if (item.do && item.do.id === removedStepId) {
				console.log(item.do, item.do.id, item.do.no, removedStepId);
				completed = false;
				// 删除节点时，置空关联的父节点
				if (!steps[item.do.id] && type === 'REMOVE_WORK_STEP') {
					console.log('filtered : ', steps, item);
					item.do.id = '';
					item.do.no = '';
				}
			} else if (item.id === removedStepId) {
				completed = false;
				// 删除节点时，置空关联的父节点
				if (!steps[item.id] && type === 'REMOVE_WORK_STEP') {
					console.log('filtered : ', steps, item);
					item.id = '';
					item.no = '';
				}
			} else if (item.do && item.do.after && item.do.after.id === removedStepId) {
				completed = false;
				// 删除节点时，置空关联的父节点
				if (!steps[item.do.after.id] && type === 'REMOVE_WORK_STEP') {
					console.log('filtered : ', steps, item);
					item.do.after.id = '';
					item.do.after.no = '';
				}
			} else if (item.after && item.after.after && item.after.after.id === removedStepId) {
				completed = false;
				// 删除节点时，置空关联的父节点
				if (!steps[item.after.after.id] && type === 'REMOVE_WORK_STEP') {
					console.log('filtered : ', steps, item);
					item.after.after.id = '';
					item.after.after.no = '';
				}
			}
		});
	}

	// 删除汇总确认中的确认项和业务导航,  先删除其他模板 又指向草稿节点， 节点内容推送 下拉选择的模板名被清空 出现空格
	// 2014 需求 Ai坐席工作流模块“信息汇总”可支持除：“信息汇总、”“转人工”、“结束模板”、“场景转接”、以外所有模板的内容信息汇总。
	if (
		(step.type === 'messageSummary' &&
			(removedStepId.includes('messageSure') ||
				removedStepId.includes('ivvr') ||
				removedStepId.includes('speakWaiting') ||
				removedStepId.includes('detectLivingFace') ||
				removedStepId.includes('faceCompare') ||
				removedStepId.includes('messageCollect') ||
				removedStepId.includes('silentWating') ||
				removedStepId.includes('requestLocation') ||
				removedStepId.includes('scanCode') ||
				removedStepId.includes('sendMessage') ||
				removedStepId.includes('signContract') ||
				removedStepId.includes('idCardCompare') ||
				removedStepId.includes('sendForm') ||
				removedStepId.includes('optionJudge') ||
				removedStepId.includes('sendContents') ||
				removedStepId.includes('nlp') ||
				removedStepId.includes('soundReception') ||
				removedStepId.includes('variableJudge'))) ||
		removedStepId.includes('sendDeposition')
	) {
		if (step.summary && step.summary.length > 0) {
			let deleteIndex = -1;
			step.summary.map((item, index) => {
				if (item.node === removedStepId) {
					deleteIndex = index;
				}
			});
			if (deleteIndex >= 0) {
				step.summary.splice(deleteIndex, 1);
			}
			if (step.summary.length === 0) {
				completed = false;
			}
		}
	}

	// 汇总确认是否前前端确认项
	if (step.type === 'summaryConfirmNode') {
		console.log(step.summary);
		if (step.summary.items.length === 0) {
			completed = false;
		}
	}

	// 查阅等待
	if (step.type === 'waitingReadNode') {
		if (step.fromNode === removedStepId) {
			completed = false;
		}
	}

	// 汇总确认确认项是否在该节点的主分支中
	if (completed && type === 'VERIFY_MESSAGE_SUMMARY' && step.type === 'messageSummary') {
		if (step.summary.length === 0) {
			completed = false;
		} else {
			const mainBranchesText = _.cloneDeep(mainBranches);
			const summary = step.summary;
			for (const item of summary) {
				// 确认节点是否在主分支中
				if (!mainBranchesText.includes(item.node)) {
					step.summary.splice(step.summary.indexOf(item), 1);
					// 信息汇总找父节点，找到删除
					break;
				}
				// 确认节点和汇总确认是否在同一分支中
				let found = false;
				for (const branch of mainBranches) {
					if (branch.includes(step.id) && branch.includes(item.node) && branch.indexOf(step.id) > branch.indexOf(item.node)) {
						found = true;
					}
				}
				if (!found) {
					step.summary.splice(step.summary.indexOf(item), 1);
					break;
				}
			}
			if (step.summary.length === 0) {
				completed = false;
			}
		}
	}
	console.log('completed : ', completed, removedStepId, type);
	return completed;
};

const verifyAllCompletion = function (steps, removedStepId, type) {
	const startNode = window.store.state.workflow.startNode;
	mainBranches.slice(0, mainBranches.length);
	mainBranches.concat(getBranches(steps, startNode));

	_.forEach(steps, function (step) {
		try {
			if (typeof step === 'object') {
				step.completed = verifyCompletion(step, steps, removedStepId, type);
			}
		} catch (error) {
			console.error('verifyAllCompletion error : ', error);
		}
	});
};

// 筛选全部的下一步
function flattenObject(ob, jumpTo) {
	for (let i in ob) {
		if (!ob.hasOwnProperty(i)) {
			continue;
		}

		if (typeof ob[i] === 'object' && ob[i] !== null) {
			let flatObject = flattenObject(ob[i], jumpTo);
			for (let x in flatObject) {
				if (!flatObject.hasOwnProperty(x)) {
					continue;
				}
			}
		} else if (ob[i] === 'jump-to') {
			jumpTo.push(ob);
		}
	}
}
// get all  parent  nodes of a spacial node
const getAllParentNodes = function (worksteps, node) {
	function findRoot(ob, nodeName, parents) {
		for (let item in ob) {
			for (let i = 0; i < ob[item].length; i++) {
				if (ob[item][i].id === nodeName && !ob[item][i].processed) {
					parents.push(item);
					ob[item][i].processed = true;
					findRoot(ob, item, parents);
					break;
				}
			}
		}
	}

	let flatten = {};
	for (let item in worksteps) {
		let jumpTo = [];
		flattenObject(worksteps[item], jumpTo);
		if (jumpTo.length > 0) {
			flatten[item] = jumpTo;
		}
	}

	flatten = _.cloneDeep(flatten);

	let parents = [];
	findRoot(flatten, node, parents);
	console.log(parents, 'parents');
	return parents;
};

// 获取主流程的节点（去除草稿节点）
const getAllMainNodes = function (worksteps, startNodeId) {
	function findChildren(ob, startNode, children) {
		for (let i = 0; i < startNode.length; i++) {
			let repeat = false;
			const nodeId = startNode[i].id;
			if (nodeId && nodeId.length > 0 && !startNode[i].processed) {
				if (children.includes(nodeId)) {
					repeat = true;
				} else {
					children.push(nodeId);
				}
			}
			if (startNode[i] && ob[nodeId] && !repeat) {
				startNode[i].processed = true;
				findChildren(ob, ob[nodeId], children);
			}
		}
	}

	let flatten = {};
	for (let item in worksteps) {
		let jumpTo = [];
		flattenObject(worksteps[item], jumpTo);
		if (jumpTo.length > 0) {
			flatten[item] = jumpTo;
		}
	}
	flatten = _.cloneDeep(flatten);
	console.log('flatten : ', flatten);

	if (!flatten[startNodeId]) {
		return [];
	}

	let children = [];
	children.push(startNodeId);
	findChildren(flatten, flatten[startNodeId], children);
	// 确认是否有遗漏节点（反向连接的节点）
	const leftNodes = Object.keys(worksteps).filter(function (item) {
		return children.indexOf(item) === -1;
	});

	// eslint-disable-next-line no-unused-expressions
	leftNodes &&
		leftNodes.map(item => {
			let found = false;
			// eslint-disable-next-line no-unused-expressions
			flatten[item] &&
				flatten[item].map(node => {
					if (children.includes(node.id)) {
						found = true;
					}
				});
			if (found) {
				children.push(item);
			}
		});

	console.log('children : ', children);
	return children;
};

// 获取主流程的所有分支（去除草稿节点）
const getAllMainBranches = function (worksteps, startNodeId) {
	let flatten = {};
	for (let item in worksteps) {
		let jumpTo = [];
		flattenObject(worksteps[item], jumpTo);
		if (jumpTo.length > 0) {
			flatten[item] = jumpTo;
		}
	}
	flatten = _.cloneDeep(flatten);

	if (!flatten[startNodeId]) {
		return [];
	}

	let result = [];
	function getPath(nodes, arr, nodeId) {
		let repeat = false;
		// TODO : 去掉未设置的 节点
		// if (nodeId && nodeId.length > 0) {
		// }
		// 如果循环，退出递归
		if (arr.includes(nodeId)) {
			repeat = true;
			arr.push(nodeId);
		} else {
			arr.push(nodeId);
		}
		if (nodes[nodeId] && nodes[nodeId].length > 0 && !repeat) {
			nodes[nodeId].forEach(v2 => getPath(nodes, [...arr], v2.id));
		} else {
			result.push(arr);
		}
	}
	getPath(flatten, [], startNodeId);

	console.log('getAllMainBranches ', result);

	return result;
};

/**
 * 判断两个数组是否相等
 * @param {array} arr1
 * @param {array} arr2
 * @returns { boolean }
+ */

function equalsArray(arr1, arr2) {
	if (arr1.length !== arr2.length) {
		return false;
	}
	for (let i = 0; i < arr1.length; ++i) {
		if (arr1[i] !== arr2[i]) {
			return false;
		}
	}
	return true;
}

// 设置节点 hasStartNode 属性
const setNodeHasStartNode = (worksteps, startNode) => {
	let data = { ...worksteps };

	for (let key in data) {
		if (key === startNode) {
			data[key].hasStartNode = true;
		} else if (typeof data[key] === 'object') {
			let parentNode = getAllParentNodes(data, key);
			if (parentNode.includes(startNode)) {
				data[key].hasStartNode = true;
			} else {
				data[key].hasStartNode = false;
			}
		}
	}

	console.log('update start node', data);

	return data;
};

// 获取路径分组，传入一维数组，返回多维数组
const getGroupByBranches = function (worksteps, startNodeId, nodes, summaryNodeId) {
	if (!nodes || nodes.length <= 0) {
		return [];
	}
	if (nodes.length === 1) {
		return [[nodes]];
	}

	const branches = getAllMainBranches(worksteps, startNodeId);
	let result = [];
	branches.forEach(branch => {
		if (branch.includes(summaryNodeId)) {
			// 截取summaryNodeId之前的元素
			branch.splice(branch.indexOf(summaryNodeId));
			// console.log(branch);
			let found = false;
			nodes.forEach(item => {
				if (branch.includes(item)) {
					found = true;
				}
			});
			if (branch.length > 0 && found) {
				found = false;
				result.forEach(element => {
					found = equalsArray(element, branch);
				});
				if (!found) {
					result.push(branch);
				}
			}
		}
	});
	console.log('group : ', result);
	return result;
};

// 筛选全部的下一步(包含type)
function flattenObjectIncType(ob, jumpTo, parent) {
	for (let i in ob) {
		if (!ob.hasOwnProperty(i)) {
			continue;
		}

		if (typeof ob[i] === 'object' && ob[i] !== null) {
			let flatObject = flattenObjectIncType(ob[i], jumpTo, ob);
			for (let x in flatObject) {
				if (!flatObject.hasOwnProperty(x)) {
					continue;
				}
			}
		} else if (ob[i] === 'jump-to') {
			ob.type = parent.type;
			jumpTo.push(ob);
		}
	}
}

// 获取ScanCode错误流程的所有分支
const getScanCodeErrorBranches = function (worksteps, startNodeId) {
	let flatten = {};
	for (let item in worksteps) {
		let jumpTo = [];
		flattenObjectIncType(worksteps[item], jumpTo);
		if (jumpTo.length > 0) {
			flatten[item] = jumpTo;
		}
	}
	flatten = _.cloneDeep(flatten);

	if (!flatten[startNodeId]) {
		return [];
	}

	// 过滤 scanCode error 节点
	let filteredFlatten = {};
	let scanCodeNodes = [];
	for (let key in flatten) {
		if (key.includes('scanCode')) {
			let items = [];
			flatten[key].map(item => {
				if (item.type === 'no' || item.type === 'other') {
					items.push(item);
				}
			});
			filteredFlatten[key] = items;
			scanCodeNodes.push(key);
		} else {
			filteredFlatten[key] = flatten[key];
		}
	}

	let result = [];

	function getPath(nodes, arr, nodeId) {
		let repeat = false;
		// 如果循环，退出递归
		if (arr.includes(nodeId)) {
			repeat = true;
			arr.push(nodeId);
		} else {
			arr.push(nodeId);
		}
		if (nodes[nodeId] && nodes[nodeId].length > 0 && !repeat && !nodeId.includes('waitingRead')) {
			for (let i = 0; i < nodes[nodeId].length; i++) {
				getPath(nodes, [...arr], nodes[nodeId][i].id);
			}
		} else {
			result.push(arr);
		}
	}
	let startTime = new Date();
	for (let i = 0; i < scanCodeNodes.length; i++) {
		getPath(filteredFlatten, [], scanCodeNodes[i]);
	}

	let endTime = new Date();
	console.log('total: ', endTime - startTime);

	// 扩充循环项
	for (let index = 0; index < result.length; index++) {
		if (result[index][0] === result[index][result[index].length - 1]) {
			result.map((subItem, subIndex) => {
				if (subIndex > index && subItem[0] === result[index][0] && subItem.join(',').includes('waitingRead')) {
					result[index] = result[index].concat(subItem);
				}
			});
		}
	}

	console.log('getScanCodeErrorBranches : ', result);
	return result;
};

/**
 *
 * @param { String } node 节点名
 * @returns { Boolean } true or false
 */
const isBlankNode = function (node) {
	if (typeof node === 'string') {
		if (node.startsWith('BlankNode_')) {
			return true;
		}
	}
	return false;
};

const isWaitingReadNode = function (node) {
	if (typeof node === 'string') {
		if (node.startsWith('waitingRead_')) {
			return true;
		}
	}
	return false;
};

const isSummaryConfirmNode = function (node) {
	if (typeof node === 'string') {
		if (node.startsWith('summaryConfirm_')) {
			return true;
		}
	}
	return false;
};

const isSendFormNode = function (node) {
	if (typeof node === 'string') {
		if (node.startsWith('sendForm_')) {
			return true;
		}
	}
	return false;
};

const getWaitingRead = function (options, steps) {
	let result = '';
	if (options.length === 0) {
		result = '';
	}
	options.forEach(item => {
		if (item.taskId === steps.fromNode) {
			result = item.taskId;
		}
	});
	return result;
};

// 校验表单模板名称
const validateTemplateName = (rule, value, callback) => {
	let allTemplateName = window.store.state.workflow.allTemplateName;
	if (!value) {
		return callback();
	}
	if (value.length < 4 || value.length > 12) {
		return callback(new Error('* 可输入长度为 4-12 位字符'));
	}
	if (allTemplateName.indexOf(value) !== -1) {
		return callback(new Error('* 当前节点名称已被使用，请重新编辑'));
	}
	return callback();
};

// 替换 HTML 片段
const replaceHtmlFragment = htmlFragment => {
	const pattern = /<(\S*?)[^>]*>.*?|<.*? \/>/gi;
	// eslint-disable-next-line no-useless-escape
	const imgPattern = /<img.*?src=[\"|\']?(.*?)[\"|\']?\s.*?>/gi;
	let fragments = htmlFragment
		.replace(imgPattern, '[图片]')
		.replace(pattern, '')
		.replace(/&nbsp;/, '')
		.replace(/&nbsp;/gi, '');
	return fragments;
};

const generatorTemplateNode = (template, videoType, audioType) => {
	let taskId = template.split('_')[0];
	if (videoType !== 'pre-recorded-video') {
		workflowTemplete[taskId + 'Node'].robotProperty = {
			needHearing: true,
			videoType: videoType,
			audioType: 'video-native'
		};
	} else if (videoType === 'pre-recorded-video') {
		workflowTemplete[taskId + 'Node'].robotProperty = {
			needHearing: true,
			videoType: videoType,
			audioType: audioType,
			silentVideo: {
				//静默视频
				id: '',
				url: '',
				content: '',
				nasPath: ''
			},
			userVideos: [
				// 该字段中放入下面用到的所有视频，不包含静默视频
				{
					id: '',
					url: '',
					content: '',
					nasPath: ''
				}
			]
		};
		workflowTemplete[taskId + 'Node'].answer.forEach(node => {
			node.do.video = { id: '', url: '', content: '' };
		});
		workflowTemplete[taskId + 'Node'].do.video = {
			id: '',
			url: '',
			content: '',
			nasPath: ''
		};
		workflowTemplete[taskId + 'Node'].errorRemind.do.video = {
			id: '',
			url: '',
			content: '',
			nasPath: ''
		};
	} else if (videoType === 'video-switching') {
		workflowTemplete[taskId + 'Node'].robotProperty = {
			needHearing: true,
			videoType: videoType,
			audioType: audioType,
			winkingUrl: {
				id: '',
				url: '',
				content: '',
				nasPath: ''
			},
			talkingUrl: {
				id: '',
				url: '',
				content: '',
				nasPath: ''
			}
		};
	} else if (videoType === 'no-video') {
		workflowTemplete[taskId + 'Node'].robotProperty = {
			needHearing: true,
			videoType: videoType,
			audioType: 'tts'
		};
	}
	return workflowTemplete[taskId + 'Node'];
};

/**
 *  获取当前信息汇总模板的父节点
 * @param {data} Object  Ai工作流所有模板
 * @param taskId String  当前节点
 * @param startNode String  第一个开始节点
 * @returns [ Array ]
 */
const getSummaryParentNodes = function (data, taskId, startNode) {
	let sures = [];
	const parentNotes = getAllParentNodes(data, taskId);
	let nodes = Array.from(new Set(parentNotes));
	if (nodes.includes(startNode)) {
		nodes = nodes.sort((a, b) => {
			return Number(a.split('_')[1]) > Number(b.split('_')[1]) ? 1 : -1;
		});
		console.log('getMessageSummaryOption : ', taskId, nodes);
		nodes.map(item => {
			if (
				item.includes('messageSure') ||
				item.includes('ivvr') ||
				item.includes('speakWaiting') ||
				item.includes('detectLivingFace') ||
				item.includes('faceCompare') ||
				item.includes('messageCollect') ||
				item.includes('silentWating') ||
				item.includes('requestLocation') ||
				item.includes('scanCode') ||
				item.includes('sendMessage') ||
				item.includes('signContract') ||
				item.includes('idCardCompare') ||
				item.includes('sendForm') ||
				item.includes('optionJudge') ||
				item.includes('sendContents') ||
				item.includes('nlp') ||
				item.includes('soundReception') ||
				item.includes('variableJudge') ||
				item.includes('sendDeposition') ||
				item.includes('sendSign') ||
				item.includes('variableCollection')
			) {
				const option = { node: item, name: getTitle(item) };
				sures.push(option);
			}
		});
	}
	return sures;
};
/**
 *  校验信息汇总
 * @param {data} Object  Ai工作流所有模板
 * @param taskId String  当前节点
 * @param startNode String  第一个开始节点
 * @returns [ Object ]
 */
const validateSummary = function (data, taskId, startNode) {
	// console.log('data :', data);
	// console.log('taskId:', taskId);
	// console.log('startNode:', startNode);
	// console.log(window.store.state);
	let worksteps = _.cloneDeep(data);
	// 求并集
	for (const key in worksteps) {
		if (key.includes('messageSummary')) {
			let union = [];
			const parents = getSummaryParentNodes(worksteps, key, startNode);
			worksteps[key].summary.forEach(item => {
				console.log('item:', item);
				parents.map(k => {
					if (item.node === k.node) {
						union.push(item);
					}
				});
			});
			worksteps[key].summary = union;
		}
	}
	return worksteps;
};

const getVariableList = (worksteps, aiVariables) => {
	if (!worksteps) {
		return [];
	}
	let variableList = [];
	// 获取AI说中的变量 "data": "{{客户ID_1806}}11{{身份证号_1808}}" .match(/\{{([^}]+)\}}/gm)
	// const text = JSON.stringify(worksteps);
	// const array = text.match(/\{{([^}]+)\}}/gm);
	// if (array && array.length > 0) {
	// 	array.map(item => {
	// 		const parts = item.split('_');
	// 		if (parts && parts.length >= 2) {
	// 			variableList.push(parseInt(parts[parts.length - 1], 10));
	// 		}
	// 	});
	// }
	// 获取模板中的引用变量
	Object.keys(worksteps).map(key => {
		const item = worksteps[key];
		if (!item) {
			return;
		}
		if (['soundReception', 'requestLocation', 'messageCollect'].includes(item.type) && item.associatedVariable && item.associatedVariable.id) {
			// associatedVariable  "id": 1806,
			variableList.push(item.associatedVariable.id);
		} else if (['messageSummary'].includes(item.type) && item.notifyExternal && item.notifyExternal.params) {
			// notifyExternal.params[{"id": 1808},{"id": 1809}]
			item.notifyExternal.params.map(param => {
				variableList.push(param.id);
			});
		} else if (['collectVariable'].includes(item.type) && item.after && item.after.data && item.after.data.data && item.after.data.data.data && item.after.data.data.data.data) {
			// "after": {"data": {"data": {"data": {"data": [{"id": 1806,},{"id": 1807,}]
			item.after.data.data.data.data.map(param => {
				variableList.push(param.id);
			});
		} else if (['variableJudgment'].includes(item.type) && item.answer) {
			// "answer": [{"conditions": [{"id": 1806,},{"id": 1808,}]},{"conditions": [{"id": 1806,},{"id": 1808,}]}]
			item.answer.map(answer => {
				if (answer.conditions) {
					answer.conditions.map(param => {
						variableList.push(param.id);
					});
				}
			});
		}
	});
	if (aiVariables) {
		variableList = variableList.concat(aiVariables);
	}
	return variableList;
};

export {
	workflowNameDic,
	getTitle,
	getAllWorkstepName,
	workflowTemplete,
	aiWorkflowRules,
	updateNodeId,
	replaceNodeId,
	verifyCompletion,
	verifyAllCompletion,
	getPreviewParent,
	getAllParentNodes,
	isBlankNode,
	isSummaryConfirmNode,
	getWaitingRead,
	getAllMainNodes,
	getAllMainBranches,
	isWaitingReadNode,
	isSendFormNode,
	getGroupByBranches,
	getScanCodeErrorBranches,
	validateTemplateName,
	replaceHtmlFragment,
	generatorTemplateNode,
	validateSummary,
	setNodeHasStartNode,
	getVariableList
};
